#ifndef B2D_SPATIAL_H
#define B2D_SPATIAL_H

#include "Maths.h"
#include "Event.h"
#include "Object.h"
#include "IUncopyable.h"

NS_B2D_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Object in 2d space.
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API Spatial
	: public Object
{
	B2D_OBJECT

public:
	typedef std::vector<Spatial*> SpatialCollection;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Transform component flag.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	enum TransformFlag
	{
		NONE		= 0,
		POSITION	= BIT(0),
		ROTATION	= BIT(1),
		SCALE		= BIT(2),
		ALL			= POSITION | ROTATION | SCALE
	};

	Event<void(Spatial&, char)> transformed;

protected:
	// Transform
	vec2 mPosition;
    float mRotation;
	vec2 mScale;
	mutable char mTransformDirty;
	mutable mat4 mTransform;

public:
	Spatial(void);
	virtual ~Spatial(void);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets the position.
	///
	/// \return	The position.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	const vec2& getPosition(void) const { return mPosition; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets the position vector.
	///
	/// \param	v	The position vector.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void setPosition(const vec2& v);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets the position.
	///
	/// \param	x	The x coordinate.
	/// \param	y	The y coordinate.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void setPosition(float x, float y);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets position x coordinate.
	///
	/// \param	x	The x coordinate.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void setPositionX(float x);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets position y coordinate.
	///
	/// \param	y	The y coordinate.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void setPositionY(float y);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Moves by object the given vector.
	///
	/// \param	dd	The distance to move.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void move(const vec2& dd);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Moves the object.
	///
	/// \param	dx	The x distance.
	/// \param	dy	The y distance.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void move(float dx, float dy);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets the rotation.
	///
	/// \return	The rotation angle.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	float getRotation(void) const { return mRotation; }
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief	Sets the rotation.
    ///
    /// \param	r	The rotation angle.
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual void setRotation(float r);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Rotates by given angle.
	///
	/// \param	dr	The angle to rotate.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void rotate(float dr);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets the scale.
	///
	/// \return	The scale.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	const vec2& getScale(void) const { return mScale; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets the scale.
	///
	/// \param	s	The scale.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void setScale(const vec2& s);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets a scale.
	///
	/// \param	sx	The x scale.
	/// \param	sy	The y scale.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void setScale(float sx, float sy);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets an uniform scale.
	///
	/// \param	s	Unified x/y scale.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void setScale(float s);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets scale x coordinate.
	///
	/// \param	sx	The x scale.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void setScaleX(float sx);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Sets scale y coordinate.
	///
	/// \param	sy	The y scale.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void setScaleY(float sy);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Scales by the given scale factor.
	///
	/// \param	ds	The scale factor.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void scale(const vec2& ds);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Scales by given factors.
	///
	/// \param	dsx	The x scale factor.
	/// \param	dsy	The y scale factor.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void scale(float dsx, float dsy);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Scales by given unified x/y scale factor.
	///
	/// \param	ds	The unified x/y scale factor.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void scale(float ds);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get matrix transform.
	/// 		This will be updated if marked dirty.
	///
	/// \return	The transform.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	const mat4& getTransform(void) const;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Convert coordinates from world space to coordinates local to this object.
	///
	/// \param	world	The world.
	///
	/// \return	.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	vec2 worldToLocal(const vec2& world) const;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Convert coordinates from local object space to world space.
	///
	/// \param	local	The local coordinates.
	///
	/// \return	The world coordinates.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	vec2 localToWorld(const vec2& local) const;

protected:
	FORCE_INLINE void markTransformDirty(char flags);

private:
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Update local transform.
	/// 		Default behaviour can be overriden.
	///
	/// \param [in,out]	transform	The transform.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void updateTransform(mat4& transform) const;
};

NS_B2D_END

#endif